<?php

/**
 * 公共基础类
 *
 * @package    modules
 * @subpackage Common
 * @author     suning <snsnsky@gmail.com>
 */

namespace Common;

class CommonBase
{
    // {{{
    /**
     * 时间: 10 分钟
     *
     * @var integer
     */
    const TEN_MINITES = 600;
    /**
     * 时间: 30 分钟
     *
     * @var integer
     */
    const HALF_HOUR = 1800;
    /**
     * 时间: 1 小时
     *
     * @var integer
     */
    const ONE_HOUR = 3600;
    /**
     * 时间: 半天
     *
     * @var integer
     */
    const HALF_DAY = 43200;
    /**
     * 时间: 1 天
     *
     * @var integer
     */
    const ONE_DAY = 86400;
    /**
     * 时间: 1 周
     *
     * @var integer
     */
    const ONE_WEEK = 604800;
    // }}}

    /**
     * 手机号码验证正则
     *
     * @var string
     */
    const REGX_MOBILE = '/^(?:130|131|132|133|134|135|136|137|138|139|147|150|151|152|153|155|156|157|158|159|170|180|181|182|183|185|186|187|188|189)\d{8}$/';

    /**
     * 列表元素个数
     *
     * @var integer
     */
    const LIST_SIZE = 10;

    /**
     * 最大缓存总数
     *
     * @var integer
     */
    const MAX_CACHE_SIZE = 1000;

    // {{{
    /**
     * 位操作: 清除位
     *
     * @var integer
     */
    const BIT_MODE_CLEAR = 1;
    /**
     * 位操作: 设置位
     *
     * @var integer
     */
    const BIT_MODE_SET = 2;
    /**
     * 图片: 无(不计算图片地址)
     *
     * @var integer
     */
    const COVER_NONE = 0;
    // }}}

    // {{{
    /**
     * 图片: 小图
     *
     * @var integer
     */
    const COVER_SMALL = 1;
    /**
     * 图片: 中图
     *
     * @var integer
     */
    const COVER_MIDDLE = 2;
    /**
     * 图片: 大图
     *
     * @var integer
     */
    const COVER_LARGE = 3;
    /**
     * 图片: 原图
     *
     * @var integer
     */
    const COVER_ORIGIN = 4;
    /**
     * 头像尺寸: 无
     *
     * @var integer
     */
    const AVATAR_NONE = 0;
    // }}}
    /**
     * 头像尺寸: 中
     *
     * @var integer
     */
    const AVATAR_MIDDLE = 100;

    // {{{
    /**
     * 头像尺寸: 大
     *
     * @var integer
     */
    const AVATAR_LARGE = 180;
    /**
     * 头像尺寸: 小
     *
     * @var integer
     */
    //const AVATAR_SMALL = 22;
    /**
     * 状态: 所有
     *
     * @var integer
     */
    const STATUS_ALL = 0;
    /**
     * 状态: 正常
     *
     * @var integer
     */
    const STATUS_APPROVED = 1;
    /**
     * 头像尺寸: 超大
     *
     * @var integer
     */
    //const AVATAR_XLARGE = 300;
    // }}}
    /**
     * 状态: 未审核
     *
     * @var integer
     */
    const STATUS_PENDING = 2;
    // }}}

    // {{{
    /**
     * 数据类型: 只取ID
     *
     * @var integer
     */
    const FETCH_ID = 0x01;
    /**
     * 数据类型: 取表中完整数据
     *
     * @var integer
     */
    const FETCH_DATA_FIELD = 0x02;
    /**
     * 数据类型: 取用户昵称
     *
     * @var integer
     */
    const FETCH_USER_NICK = 0x04;
    // }}}

    // {{{
    /**
     * 数据类型: 取用户信息
     *
     * @var integer
     */
    const FETCH_USER_DATA = 0x08;
    /**
     * 数据类型: 取城市信息
     *
     * @var integer
     */
    const FETCH_CITY_INFO = 0x10;
    /**
     * 数据类型: 取额外详细数据
     *
     * @var integer
     */
    const FETCH_EXTRA_DATA = 0x20;
    /**
     * 数据类型: 取关联数据
     *
     * @var integer
     */
    const FETCH_EXTENDS = 0x40;
    /**
     * 数据类型: 获取所有数据
     *
     * @var integer
     */
    const FETCH_ALL = 0x80;
    /**
     * 数据类型: 随机获取数据
     *
     * @var integer
     */
    const FETCH_RANDOM = 0x100;
    /**
     * 数据类型: 随去重数据
     *
     * @var integer
     */
    const FETCH_UNIQUE = 0x200;
    /**
     * 数据类型: 按其他逻辑取数据(不同方法有不同实现)
     *
     * @var integer
     */
    const FETCH_CUSTOM = 0x800;
    /**
     * 排序方式: 升序
     *
     * @var integer
     */
    const SORT_ORDER_ASC = 0;
    /**
     * 排序方式: 降序
     *
     * @var integer
     */
    const SORT_ORDER_DESC = 1;
    /**
     * 坐标: 纬度
     *
     * @var string
     */
    const COORD_LATITUDE = 'Lat';
    // }}}

    // {{{
    /**
     * 坐标: 经度
     *
     * @var string
     */
    const COORD_LONGITUDE = 'Lng';
    /**
     * 额外参数: 项目状态
     *
     * @var string
     */
    const EXTRA_STATUS = 'status';
    /**
     * 额外参数: 当前登录用户的ID
     *
     * @var string
     */
    const EXTRA_LOGIN_UID = 'uid';

    /**
     * 额外参数: 官方版本作者的ID
     *
     * @var string
     */
    const EXTRA_OFFICIAL_UID = 'officialuid';
    // }}}

    // {{{
    /**
     * 额外参数: 是否计算用户头像
     *
     * @var string
     */
    const EXTRA_AVATAR = 'avatar';
    /**
     * 额外参数: 用户数据
     *
     * @var string
     */
    const EXTRA_USER_DATA = 'user_data';
    // }}}

    // {{{
    /**
     * 额外参数: 是否计算图片URL
     *
     * @var string
     */
    const EXTRA_COVER = 'cover';
    /**
     * 额外参数: 数据的类型: 只取ID|取用户昵称|取关联数据|取额外数据|..
     *
     * @var string
     */
    const EXTRA_FETCH = 'fetch';
    /**
     * 额外参数: 是否强制刷新缓存
     *
     * @var string
     */
    const EXTRA_REFRESH = 'refresh';
    /**
     * 额外参数: 筛选包含任意一个或多个(至少一个)过滤字段
     *
     * @var string
     */
    const EXTRA_FILTER_INCLUDE = 'filter_include';
    /**
     * 额外参数: 筛选包含任意(最少为零个)过滤字段
     *
     * @var string
     */
    const EXTRA_FILTER_ANY = 'filter_any';
    /**
     * 额外参数: 筛选排除所有过滤字段
     *
     * @var string
     */
    const EXTRA_FILTER_EXCLUDE = 'filter_exclude';
    /**
     * 额外参数: 包含范围限制
     *
     * @var string
     */
    const EXTRA_RANGE_INCLUDE = 'range_include';
    /**
     * 额外参数: 排除范围限制
     *
     * @var string
     */
    const EXTRA_RANGE_EXCLUDE = 'range_exclude';
    /**
     * 额外参数: 某个参数的类别(不同方法有不同实现)
     *
     * @var string
     */
    const EXTRA_TYPE = 'type';
    /**
     * 额外参数: 要获取的字段
     *
     * @var string
     */
    const EXTRA_FIELDS = 'fields';
    /**
     * 额外参数: 是否从写库取数据
     *
     * @var string
     */
    const EXTRA_FROM_W = 'from_w';
    /**
     * 额外参数: 是否使用缓存
     *
     * @var string
     */
    const EXTRA_USE_CACHE = 'use_cache';
    /**
     * 额外参数: 返回结果集偏移(负数表不限制,取所有结果)
     *
     * @var string
     */
    const EXTRA_OFFSET = 'offset';
    /**
     * 额外参数: 返回结果集的个数(负数表示不限制,取自 EXTRA_OFFSET 开始的所有结果;当 EXTRA_OFFSET 为负数时, EXTRA_LIMIT 无意义)
     *
     * @var string
     */
    const EXTRA_LIMIT = 'limit';
    /**
     * 额外参数: 排序字段
     *
     * @var string
     */
    const EXTRA_SORT_FIELD = 'sort_field';
    /**
     * 额外参数: 排序方式
     *
     * @var string
     */
    const EXTRA_SORT_ORDER = 'sort_order';
    /**
     * 额外参数: 地理坐标
     *
     * @var string
     */
    const EXTRA_COORDINATE = 'coord';
    /**
     * 额外参数: 时间点
     *
     * @var string
     */
    const EXTRA_DATE_TIME = 'date_time';
    /**
     * 额外参数: 开始时间点
     *
     * @var string
     */
    const EXTRA_TIME_START = 'time_start';
    /**
     * 额外参数: 结束时间点
     *
     * @var string
     */
    const EXTRA_TIME_END = 'time_end';
    /**
     * 额外参数: 性别
     *
     * @var string
     */
    const EXTRA_GENDER = 'gender';
    /**
     * 额外参数: 城市
     *
     * @var string
     */
    const EXTRA_CITY = 'city';
    /**
     * 额外参数: 区域
     *
     * @var string
     */
    const EXTRA_AREA = 'area';
    /**
     * 额外参数: 同步数据
     *
     * @var string
     */
    const EXTRA_SYNC = 'sync';
    /**
     * 额外参数: 标识
     *
     * @var string
     */
    const EXTRA_FLAG = 'flag';
    /**
     * 额外参数: 调试开关
     *
     * @var string
     */
    const EXTRA_DEBUG = 'debug';
    /**
     * 返回值: 操作成功
     *
     * @var integer
     */
    const RET_CODE_SUCC = 0x00;
    /**
     * 返回值: 操作失败
     *
     * @var integer
     */
    const RET_CODE_FAIL = 0x01;
    /**
     * 返回值: 参数错误
     *
     * @var integer
     */
    const RET_CODE_WRONG_ARGS = 0x10;
    /**
     * 返回值: 缺少参数
     *
     * @var integer
     */
    const RET_CODE_LACK_ARGS = 0x11;
    // }}}
    /**
     * 返回值: 未提供合格数据
     *
     * @var integer
     */
    const RET_CODE_NO_VALID_DATA = 0x12;

    // {{{
    /**
     * 返回值: 无可供更新数据
     *
     * @var integer
     */
    const RET_CODE_NO_NEW_DATA = 0x13;
    /**
     * 返回值: 数据未更改
     *
     * @var integer
     */
    const RET_CODE_NOT_MODIFIED = 0x14;
    /**
     * 返回值: 数据不匹配
     *
     * @var integer
     */
    const RET_CODE_NOT_MATCH = 0x15;
    /**
     * 返回值: 对象不存在
     *
     * @var integer
     */
    const RET_CODE_NOT_EXISTS = 0x20;
    /**
     * 返回值: 禁止操作
     *
     * @var integer
     */
    const RET_CODE_FORBIDDEN = 0x21;
    /**
     * 返回值: 唯一键冲突
     *
     * @var integer
     */
    const RET_CODE_UNIQUE_CONFLICT = 0x22;
    /**
     * 返回值: 数目抵达上限
     *
     * @var integer
     */
    const RET_CODE_LIMITED = 0x30;
    /**
     * 返回值: 系统错误
     *
     * @var integer
     */
    const RET_CODE_SYS_ERROR = 0x31;
    /**
     * 返回值: 数据库错误
     *
     * @var integer
     */
    const RET_CODE_DB_ERROR = 0x32;
    /**
     * 返回值: 文件上传失败
     *
     * @var integer
     */
    const RET_CODE_UPLOAD_ERROR = 0x33;
    /**
     * 返回值: 文件大小错误
     *
     * @var integer
     */
    const RET_CODE_FILESIZE_ERROR = 0x34;
    /**
     * 返回值: 文件损坏
     *
     * @var integer
     */
    const RET_CODE_FILE_CORRUPTED = 0x35;
    /**
     * 返回值: 图片裁剪失败
     *
     * @var integer
     */
    const RET_CODE_CROP_ERROR = 0x36;
    /**
     * 返回值: 未知错误
     *
     * @var integer
     */
    const RET_CODE_UNKNOWN_ERROR = 0x40;
    protected static $_bit_modes = [
        self::BIT_MODE_CLEAR => 1,
        self::BIT_MODE_SET => 2,
    ];
    protected static $_cover_sizes = [
        self::COVER_SMALL => 'small',
        self::COVER_MIDDLE => 'middle',
        self::COVER_LARGE => 'big',
        self::COVER_ORIGIN => 'source',
    ];
    /**
     * 头像的图片尺寸
     *
     * @var array
     */
    protected static $_avatar_sizes = [
        //self::AVATAR_NONE => 0,
        //self::AVATAR_SMALL => 22,
        self::AVATAR_MIDDLE => 100,
        self::AVATAR_LARGE => 180,
        //self::AVATAR_XLARGE => 300,
    ];
    protected static $_sort_orders = [
        self::SORT_ORDER_ASC => 'ASC',
        self::SORT_ORDER_DESC => 'DESC',
    ];
    protected static $_extra_meta = [
        self::EXTRA_LOGIN_UID => [
            'filter' => FILTER_VALIDATE_INT,
            'default' => 0,
        ],
        self::EXTRA_OFFICIAL_UID => [
            'filter' => FILTER_VALIDATE_INT,
            'default' => 0,
        ],
        self::EXTRA_AVATAR => [
            'filter' => FILTER_VALIDATE_REGEXP,
            'options' => [
                'regexp' => '/^(?:0|100|180)$/',
            ],
            'default' => 100,
        ],
        self::EXTRA_FETCH => [
            'filter' => FILTER_VALIDATE_INT,
            'options' => [
                'min_range' => self::FETCH_ID,
            ],
            'default' => self::FETCH_DATA_FIELD,
        ],
        self::EXTRA_FROM_W => [
            'filter' => FILTER_VALIDATE_BOOLEAN,
            'default' => false,
        ],
        self::EXTRA_REFRESH => [
            'filter' => FILTER_VALIDATE_BOOLEAN,
            'default' => false,
        ],
        self::EXTRA_OFFSET => [
            'filter' => FILTER_VALIDATE_INT,
            'default' => 0,
        ],
        self::EXTRA_LIMIT => [
            'filter' => FILTER_VALIDATE_INT,
            'default' => self::LIST_SIZE,
        ],
        self::EXTRA_STATUS => [
            'filter' => FILTER_VALIDATE_INT,
            'default' => 1,
        ],
    ];
    // }}}
    protected static $_ret_map = [
        self::RET_CODE_SUCC => [
            'code' => self::RET_CODE_SUCC,
            'msg' => '操作成功',
        ],
        self::RET_CODE_FAIL => [
            'code' => self::RET_CODE_FAIL,
            'msg' => '操作失败',
        ],
        self::RET_CODE_WRONG_ARGS => [
            'code' => self::RET_CODE_WRONG_ARGS,
            'msg' => '参数错误',
        ],
        self::RET_CODE_LACK_ARGS => [
            'code' => self::RET_CODE_LACK_ARGS,
            'msg' => '缺少参数',
        ],
        self::RET_CODE_NO_VALID_DATA => [
            'code' => self::RET_CODE_NO_VALID_DATA,
            'msg' => '未提供合格数据',
        ],
        self::RET_CODE_NO_NEW_DATA => [
            'code' => self::RET_CODE_NO_NEW_DATA,
            'msg' => '无可供更新数据',
        ],
        self::RET_CODE_NOT_MODIFIED => [
            'code' => self::RET_CODE_NOT_MODIFIED,
            'msg' => '数据未更改',
        ],
        self::RET_CODE_NOT_MATCH => [
            'code' => self::RET_CODE_NOT_MATCH,
            'msg' => '数据不匹配',
        ],
        self::RET_CODE_NOT_EXISTS => [
            'code' => self::RET_CODE_NOT_EXISTS,
            'msg' => '对象不存在',
        ],
        self::RET_CODE_FORBIDDEN => [
            'code' => self::RET_CODE_FORBIDDEN,
            'msg' => '禁止操作',
        ],
        self::RET_CODE_UNIQUE_CONFLICT => [
            'code' => self::RET_CODE_UNIQUE_CONFLICT,
            'msg' => '唯一索引冲突',
        ],
        self::RET_CODE_LIMITED => [
            'code' => self::RET_CODE_LIMITED,
            'msg' => '数目抵达上限',
        ],
        self::RET_CODE_SYS_ERROR => [
            'code' => self::RET_CODE_SYS_ERROR,
            'msg' => '系统错误',
        ],
        self::RET_CODE_DB_ERROR => [
            'code' => self::RET_CODE_DB_ERROR,
            'msg' => '数据库错误',
        ],
        self::RET_CODE_UPLOAD_ERROR => [
            'code' => self::RET_CODE_UPLOAD_ERROR,
            'msg' => '文件上传失败',
        ],
        self::RET_CODE_FILESIZE_ERROR => [
            'code' => self::RET_CODE_FILESIZE_ERROR,
            'msg' => '文件大小错误',
        ],
        self::RET_CODE_FILE_CORRUPTED => [
            'code' => self::RET_CODE_FILE_CORRUPTED,
            'msg' => '文件损坏',
        ],
        self::RET_CODE_CROP_ERROR => [
            'code' => self::RET_CODE_CROP_ERROR,
            'msg' => '图片裁剪失败',
        ],
        self::RET_CODE_UNKNOWN_ERROR => [
            'code' => self::RET_CODE_UNKNOWN_ERROR,
            'msg' => '未知错误',
        ],
    ];

    /**
     * 较验额外参数
     *
     * @param array $extra 参数列表
     * @param array $metas 较验模板,若为空则使用默认的
     * @return array
     */
    protected static function _parseExtras($extra, $metas = null)
    {
        $metas = empty($metas) ? static::$_extra_meta : $metas;
        $args = filter_var_array((array)$extra, $metas);

        foreach ($metas as $fkey => $fval) {
            $val = isset($args[$fkey]) ? $args[$fkey] : null;
            if ($val === null || ($val === false && $fval['filter'] !== FILTER_VALIDATE_BOOLEAN)) {
                $args[$fkey] = $fval['default'];
            }
        }

        return $args;
    }

    /**
     * 处理计数字段更新
     *
     * @param mixed $info   要更新的数据
     * @param array $fields 计数字段schema
     * @return array
     */
    protected static function _parseCount($info, $fields)
    {
        $data = [];

        if (!is_array($info) || !is_array($fields)) {
            return $data;
        }
        $info = array_intersect_key($info, $fields);
        foreach ($info as $type => $seed) {
            if (isset($fields[$type]) && is_numeric($seed)) {
                $field = $fields[$type];
                $seed_int = floatval($seed);

                $seed_str = "{$seed}";
                $sign = $seed_str[0];

                if ($seed_int !== 0) {
                    // 在原值基础上进行加减
                    if ($sign === '+' || $sign === '-') {
                        $expr = $seed_int > 0 ? "`{$field}`+{$seed_int}" : "GREATEST(`{$field}`{$seed_int}, 0)";
                    } else {
                        $expr = $seed_int;
                    }

                    $data[$field] = "`{$field}` = {$expr}";
                } else {
                    if ($sign !== '+' && $sign !== '-') {
                        $data[$field] = "`{$field}` = 0";
                    }
                }
            }
        }

        return $data;
    }

    /**
     * 处理计数字段更新(可为负数)
     *
     * @param mixed $info   要更新的数据
     * @param array $fields 计数字段schema
     * @return array
     */
    protected static function _parseNegativeCount($info, $fields)
    {
        $data = [];

        if (!is_array($info) || !is_array($fields)) {
            return $data;
        }
        $info = array_intersect_key($info, $fields);
        foreach ($info as $type => $seed) {
            if (isset($fields[$type]) && is_numeric($seed)) {
                $field = $fields[$type];
                $seed_int = floatval($seed);

                $seed_str = "{$seed}";
                $sign = $seed_str[0];

                if ($seed_int !== 0) {
                    // 在原值基础上进行加减
                    if ($sign === '+' || $sign === '-') {
                        $expr = $seed_int > 0 ? "`{$field}`+{$seed_int}" : "`{$field}`{$seed_int}";
                    } else {
                        $expr = $seed_int;
                    }

                    $data[$field] = "`{$field}` = {$expr}";
                } else {
                    if ($sign !== '+' && $sign !== '-') {
                        $data[$field] = "`{$field}` = 0";
                    }
                }
            }
        }

        return $data;
    }

    /**
     * 净化字符串
     *
     * @param string  $str        目标字符串
     * @param integer $len        截取指定长度,为-1时表不截取
     * @param boolean $strip_tags 是否剔除HTML标签,默认不剔除
     * @return string
     */
    protected static function _sanitizeString($str, $len = - 1, $strip_tags = false, $quote = true)
    {
        $pured_str = iconv(mb_detect_encoding($str), 'UTF-8', $str);
        $strip_tags && $pured_str = trim(strip_tags($pured_str));
        $len > 0 && $pured_str = trim(mb_substr($pured_str, 0, $len, 'utf8'));
        $quote && $pured_str = mysql_quote($pured_str);

        if (empty($pured_str)) {
            $trace = debug_backtrace();
            foreach ($trace as &$t) {
                if (isset($t['object'])) {
                    unset($t['object']);
                }
            }
            runtime_log('CommonBase/_sanitizeString', serialize([
                '$str' => $str,
                '$pured_str' => $pured_str,
                '$trace' => $trace
            ]));
        }

        return $pured_str;
    }
}
